//
//  BC_Hashtable.m
//  CocOth
//
//  Created by BrunoCausse on 11/03/11.
//  Copyright 2011 poulailler cie. All rights reserved.
//

#import "BC_Hashtable.h"

#import "BC_Const.h"

#import "BC_Hashentry.h"
#import "BC_Hashrecord.h"
#import "BC_Hashvalue.h"

#import "BC_Board.h"



@implementation BC_Hashtable

- (id)initWithNumberOfBits:(unsigned int)n
{
    self = [super init];
    if (self) {
        // Initialization code here.
        
        capacity = (1UL<<n);
        mask  = (1<<n)-1;
        
        table = [[NSMutableArray alloc] initWithCapacity:capacity];
        
        for (unsigned int i = 0; i < capacity; i++) {
            [table addObject:[[[BC_Hashentry alloc] init] autorelease]];
        }
        
    }
    
    return self;
}

- (void)dealloc
{
    [table release];
    [super dealloc];
}

- (void)clear
{
    
    for (unsigned int i = 0; i < capacity; i++) {
        
        BC_Hashentry *entry = [table objectAtIndex:i];
        
        BC_Hashrecord *deepest = [entry deepest];
        [deepest setLock:0];
        
        BC_Hashrecord *newest = [entry newest];
        [newest setLock:0];

    }

    
}

- (BC_Hashvalue *)getWithBoard:(BC_Board *)board
{
    unsigned long long hashcode = [board hashcode];
    
    BC_Hashentry *entry = [table objectAtIndex:(hashcode & mask)];
    
    BC_Hashrecord *deepest = [entry deepest];
    unsigned long long packed = [deepest packed];
    unsigned long long lock = [deepest lock] ^ packed;
    if(lock == hashcode) {
        return [[[BC_Hashvalue alloc] initWithPacked:packed] autorelease];
    }
    
    BC_Hashrecord *newest = [entry newest];
    packed = [newest packed];
    lock = [newest lock] ^ packed;
    if(lock == hashcode) {
        return [[[BC_Hashvalue alloc] initWithPacked:packed] autorelease];
    }
    
    return nil;
    
}

- (void)updateWithBoard:(BC_Board *)board
            selectivity:(unsigned char)select
                  depth:(unsigned char)aDepth
                 square:(unsigned char)move
                  alpha:(int)lower
                   beta:(int)upper
                  score:(int)value
{
    
    unsigned long long hashcode = [board hashcode];
    BC_Hashentry *entry = [table objectAtIndex:(hashcode & mask)];
    
    BC_Hashrecord *deepest = [entry deepest];
    unsigned long long deepest_packed = [deepest packed];
    unsigned long long deepest_lock = [deepest lock];
    unsigned long long deepest_hashcode = deepest_packed ^ deepest_lock;
    
    BC_Hashvalue *deepest_value = [[BC_Hashvalue alloc] initWithPacked:deepest_packed];
    
    /* try to update deepest entry */
    if(hashcode == deepest_hashcode && select == [deepest_value selectivity] && aDepth == [deepest_value depth] && lower < value) {
        
        if (value < upper && value < [deepest_value upper]) 
			[deepest_value setUpper:(short)value];
		if (value > lower && value > [deepest_value lower])
			[deepest_value setLower:(short)value];
    
        /* control if lower>upper */	
        if([deepest_value lower] > [deepest_value upper]) {
            
            if(value<upper)
                [deepest_value setUpper:(short)value];
            else
                [deepest_value setUpper:MAXSCORE];
            
            if(value>lower)
                [deepest_value setLower:(short)value];
            else
                [deepest_value setLower:MINSCORE];
        }
        
        [deepest_value setMove:move];
        
        deepest_packed = [deepest_value wide_2_compact];
        deepest_lock   = hashcode ^ deepest_packed;
        
        [deepest setPacked:deepest_packed];
        [deepest setLock:deepest_lock];

    } else {
        
        BC_Hashrecord *newest = [entry newest];
        unsigned long long newest_packed = [newest packed];
        unsigned long long newest_lock = [newest lock];
        unsigned long long newest_hashcode = newest_packed ^ newest_lock;
        
        BC_Hashvalue *newest_value = [[BC_Hashvalue alloc] initWithPacked:newest_packed];
        
        /* else try to update newest entry */
		if (hashcode == newest_hashcode && select == [newest_value selectivity]  && aDepth == [newest_value depth] && lower < value) {
            
            if (value < upper && value < [newest_value upper]) 
                [newest_value setUpper:(short)value];
            if (value > lower && value > [newest_value lower])
                [newest_value setLower:(short)value];
            
            /* control if lower>upper */	
            if([newest_value lower] > [newest_value upper]) {
                
                if(value<upper)
                    [newest_value setUpper:(short)value];
                else
                    [newest_value setUpper:MAXSCORE];
                
                if(value>lower)
                    [newest_value setLower:(short)value];
                else
                    [newest_value setLower:MINSCORE];
            }
            
            [newest_value setMove:move];
            
            newest_packed = [newest_value wide_2_compact];
            newest_lock   = hashcode ^ newest_packed;

            [newest setPacked:newest_packed];
            [newest setLock:newest_lock];


        } else if  (deepest_hashcode == hashcode ||
                   ([deepest_value depth] <  aDepth) ||
                   ([deepest_value depth] == aDepth && [deepest_value selectivity] < select)) { // priority 
        
            if(deepest_hashcode != hashcode &&  (newest_hashcode == hashcode ||
                                                ([newest_value depth] <  [deepest_value depth]) ||
                                                ([newest_value depth] == [deepest_value depth] && [newest_value selectivity] <= [deepest_value selectivity]))) {
            
                //copy
                [newest setLock:deepest_lock];
                [newest setPacked:deepest_packed];
            }
        
            [deepest_value setDepth:aDepth];
            [deepest_value setSelectivity:select];
            [deepest_value setLower:MINSCORE];
            [deepest_value setUpper:MAXSCORE];
            
            if (value < upper) [deepest_value setUpper:(short)value];
            if (value > lower) [deepest_value setLower:(short)value];
            
            [deepest_value setMove:move];
            
            deepest_packed = [deepest_value wide_2_compact];
            deepest_lock   = hashcode ^ deepest_packed;

            [deepest setPacked:deepest_packed];
            [deepest setLock:deepest_lock];
            
        } else {
			
			[newest_value setDepth:aDepth];
			[newest_value setSelectivity:select];
			[newest_value setLower:MINSCORE];
			[newest_value setUpper:MAXSCORE];
            
			if (value < upper) [newest_value setUpper:(short)value];
			if (value > lower) [newest_value setLower:(short)value];
            
			[newest_value setMove:move];
			
            newest_packed = [newest_value wide_2_compact];
            newest_lock   = hashcode ^ newest_packed;

            [newest setPacked:newest_packed];
            [newest setLock:newest_lock];

		}
        
        [newest_value release];

    }
    
    [deepest_value release];
    
}




@end
